import { html } from 'lit'; import { unsafeHTML } from 'lit/directives/unsafe-html.js'; import { customElement } from 'lit/decorators.js'; import { LitroPage } from '@beatzball/litro/runtime'; import { definePageData } from '@beatzball/litro'; import { createError } from 'h3'; import type { Post } from 'litro:content'; import { getPosts } from 'litro:content'; import { siteConfig } from '../../server/starlight.config.js'; import { extractHeadings, addHeadingIds } from '../../src/extract-headings.js'; import { starlightHead } from '../../src/route-meta.js'; import { formatDate, isoDate } from '../../src/date-utils.js'; // Register components used in render() import '../../src/components/starlight-header.js'; export interface BlogPostData { post: Post; body: string; toc: Array<{ depth: number; text: string; slug: string }>; siteTitle: string; nav: typeof siteConfig.nav; } export const pageData = definePageData(async (event) => { const slug = event.context.params?.slug ?? ''; // Content URLs are /content/blog/ (contentDir = 'content') const posts = await getPosts(); const post = posts.find(p => p.url === `/content/blog/${slug}`); if (!post) { throw createError({ statusCode: 404, message: `Post not found: ${slug}` }); } const toc = extractHeadings(post.rawBody); const body = addHeadingIds(post.body); return { post, body, toc, siteTitle: siteConfig.title, nav: siteConfig.nav, } satisfies BlogPostData; }); export async function generateRoutes(): Promise { const posts = await getPosts(); return posts .filter(p => p.url.startsWith('/content/blog/')) .map(p => '/blog' + p.url.slice('/content/blog'.length)); } export const routeMeta = { head: starlightHead, title: 'Blog — {{projectName}}', }; @customElement('page-blog-slug') export class BlogPostPage extends LitroPage { override render() { const data = this.serverData as BlogPostData | null; if (!data?.post) return html`

Loading…

`; const { post, body, siteTitle, nav } = data; const blogSlug = post.url.slice('/content/blog/'.length); return html`

${post.title}

${post.tags.filter(t => t !== 'posts').length > 0 ? html`
${post.tags.filter(t => t !== 'posts').map(tag => html` #${tag} `)}
` : ''}
${unsafeHTML(body)}
`; } } export default BlogPostPage;